"""
熔断器模式 - 防止级联故障，提高系统稳定性
支持半开状态、自动恢复、指标监控等功能
"""

import time
import asyncio
import threading
from typing import (
    Callable, Optional, Any, Dict, List, Union,
    Awaitable, TypeVar, Protocol, runtime_checkable
)
from dataclasses import dataclass, field
from enum import Enum
from abc import ABC, abstractmethod
from datetime import datetime, timedelta
import functools

from ..logging.manager import get_logger
from ..events.bus import get_event_bus, Event

# 类型变量
F = TypeVar('F', bound=Callable[..., Any])
AsyncF = TypeVar('AsyncF', bound=Callable[..., Awaitable[Any]])

class CircuitBreakerState(Enum):
    """熔断器状态"""
    CLOSED = "closed"         # 关闭状态 - 正常工作
    OPEN = "open"             # 开启状态 - 熔断生效
    HALF_OPEN = "half_open"   # 半开状态 - 尝试恢复

@dataclass
class CircuitBreakerConfig:
    """熔断器配置"""
    failure_threshold: int = 5              # 失败阈值
    success_threshold: int = 3              # 成功阈值（半开状态）
    timeout: float = 60.0                   # 熔断超时时间（秒）
    expected_exception: type = Exception    # 预期异常类型
    monitor_window: float = 60.0           # 监控窗口时间（秒）
    min_requests: int = 5                  # 最小请求数
    failure_rate_threshold: float = 0.5    # 失败率阈值

@dataclass
class CircuitBreakerStats:
    """熔断器统计信息"""
    total_requests: int = 0
    successful_requests: int = 0
    failed_requests: int = 0
    last_failure_time: Optional[float] = None
    last_success_time: Optional[float] = None
    consecutive_failures: int = 0
    consecutive_successes: int = 0
    state_transitions: int = 0
    
    @property
    def failure_rate(self) -> float:
        """失败率"""
        if self.total_requests == 0:
            return 0.0
        return self.failed_requests / self.total_requests
    
    @property
    def success_rate(self) -> float:
        """成功率"""
        if self.total_requests == 0:
            return 0.0
        return self.successful_requests / self.total_requests

class CircuitBreakerError(Exception):
    """熔断器异常"""
    pass

class CircuitBreakerOpenError(CircuitBreakerError):
    """熔断器开启异常"""
    pass

class CircuitBreaker:
    """熔断器实现"""
    
    def __init__(self, name: str, config: CircuitBreakerConfig = None):
        self.name = name
        self.config = config or CircuitBreakerConfig()
        self.state = CircuitBreakerState.CLOSED
        self.stats = CircuitBreakerStats()
        self.last_failure_time = 0.0
        self.last_state_change = time.time()
        self._lock = threading.Lock()
        self.logger = get_logger(f"circuit_breaker.{name}")
        self.event_bus = get_event_bus()
        
        # 时间窗口内的请求记录
        self._requests: List[tuple] = []  # (timestamp, success)
    
    def _cleanup_old_requests(self):
        """清理过期的请求记录"""
        current_time = time.time()
        cutoff_time = current_time - self.config.monitor_window
        self._requests = [(ts, success) for ts, success in self._requests if ts > cutoff_time]
    
    def _update_stats_from_window(self):
        """从时间窗口更新统计信息"""
        self._cleanup_old_requests()
        
        total = len(self._requests)
        successful = sum(1 for _, success in self._requests if success)
        failed = total - successful
        
        self.stats.total_requests = total
        self.stats.successful_requests = successful
        self.stats.failed_requests = failed
    
    def _should_trip(self) -> bool:
        """判断是否应该触发熔断"""
        self._update_stats_from_window()
        
        # 检查最小请求数
        if self.stats.total_requests < self.config.min_requests:
            return False
        
        # 检查失败率
        if self.stats.failure_rate >= self.config.failure_rate_threshold:
            return True
        
        # 检查连续失败次数
        if self.stats.consecutive_failures >= self.config.failure_threshold:
            return True
        
        return False
    
    def _can_attempt_reset(self) -> bool:
        """判断是否可以尝试重置（从开启状态到半开状态）"""
        if self.state != CircuitBreakerState.OPEN:
            return False
        
        return time.time() - self.last_state_change >= self.config.timeout
    
    def _record_success(self):
        """记录成功调用"""
        current_time = time.time()
        
        with self._lock:
            self._requests.append((current_time, True))
            self.stats.last_success_time = current_time
            self.stats.consecutive_successes += 1
            self.stats.consecutive_failures = 0
            
            # 状态转换逻辑
            if self.state == CircuitBreakerState.HALF_OPEN:
                if self.stats.consecutive_successes >= self.config.success_threshold:
                    self._change_state(CircuitBreakerState.CLOSED)
    
    def _record_failure(self):
        """记录失败调用"""
        current_time = time.time()
        
        with self._lock:
            self._requests.append((current_time, False))
            self.stats.last_failure_time = current_time
            self.stats.consecutive_failures += 1
            self.stats.consecutive_successes = 0
            
            # 状态转换逻辑
            if self.state == CircuitBreakerState.CLOSED:
                if self._should_trip():
                    self._change_state(CircuitBreakerState.OPEN)
            elif self.state == CircuitBreakerState.HALF_OPEN:
                self._change_state(CircuitBreakerState.OPEN)
    
    def _change_state(self, new_state: CircuitBreakerState):
        """改变熔断器状态"""
        old_state = self.state
        self.state = new_state
        self.last_state_change = time.time()
        self.stats.state_transitions += 1
        
        self.logger.info(f"Circuit breaker state changed: {old_state.value} -> {new_state.value}")
        
        # 发布状态变更事件
        self.event_bus.publish(Event(
            event_type=f"circuit_breaker.state_changed",
            data={
                "name": self.name,
                "old_state": old_state.value,
                "new_state": new_state.value,
                "stats": {
                    "total_requests": self.stats.total_requests,
                    "failure_rate": self.stats.failure_rate,
                    "consecutive_failures": self.stats.consecutive_failures
                }
            }
        ))
    
    def _can_execute(self) -> bool:
        """判断是否可以执行调用"""
        with self._lock:
            if self.state == CircuitBreakerState.CLOSED:
                return True
            elif self.state == CircuitBreakerState.OPEN:
                if self._can_attempt_reset():
                    self._change_state(CircuitBreakerState.HALF_OPEN)
                    return True
                return False
            elif self.state == CircuitBreakerState.HALF_OPEN:
                return True
        
        return False
    
    def call(self, func: Callable, *args, **kwargs) -> Any:
        """执行同步函数调用"""
        if not self._can_execute():
            raise CircuitBreakerOpenError(
                f"Circuit breaker '{self.name}' is open. "
                f"Last failure: {self.stats.last_failure_time}"
            )
        
        try:
            result = func(*args, **kwargs)
            self._record_success()
            return result
        except self.config.expected_exception as e:
            self._record_failure()
            raise e
    
    async def call_async(self, func: Callable, *args, **kwargs) -> Any:
        """执行异步函数调用"""
        if not self._can_execute():
            raise CircuitBreakerOpenError(
                f"Circuit breaker '{self.name}' is open. "
                f"Last failure: {self.stats.last_failure_time}"
            )
        
        try:
            result = await func(*args, **kwargs)
            self._record_success()
            return result
        except self.config.expected_exception as e:
            self._record_failure()
            raise e
    
    def get_stats(self) -> Dict[str, Any]:
        """获取统计信息"""
        with self._lock:
            self._update_stats_from_window()
            return {
                "name": self.name,
                "state": self.state.value,
                "total_requests": self.stats.total_requests,
                "successful_requests": self.stats.successful_requests,
                "failed_requests": self.stats.failed_requests,
                "failure_rate": self.stats.failure_rate,
                "success_rate": self.stats.success_rate,
                "consecutive_failures": self.stats.consecutive_failures,
                "consecutive_successes": self.stats.consecutive_successes,
                "last_failure_time": self.stats.last_failure_time,
                "last_success_time": self.stats.last_success_time,
                "state_transitions": self.stats.state_transitions,
                "last_state_change": self.last_state_change
            }
    
    def reset(self):
        """重置熔断器"""
        with self._lock:
            self._change_state(CircuitBreakerState.CLOSED)
            self.stats = CircuitBreakerStats()
            self._requests.clear()
            self.logger.info(f"Circuit breaker '{self.name}' has been reset")

class CircuitBreakerManager:
    """熔断器管理器"""
    
    def __init__(self):
        self.circuit_breakers: Dict[str, CircuitBreaker] = {}
        self.configs: Dict[str, CircuitBreakerConfig] = {}
        self._lock = threading.Lock()
        self.logger = get_logger("circuit_breaker_manager")
    
    def register_config(self, name: str, config: CircuitBreakerConfig):
        """注册熔断器配置"""
        with self._lock:
            self.configs[name] = config
            self.logger.info(f"Registered circuit breaker config: {name}")
    
    def get_circuit_breaker(self, name: str, config: CircuitBreakerConfig = None) -> CircuitBreaker:
        """获取或创建熔断器"""
        with self._lock:
            if name not in self.circuit_breakers:
                # 使用提供的配置或已注册的配置
                breaker_config = config or self.configs.get(name) or CircuitBreakerConfig()
                self.circuit_breakers[name] = CircuitBreaker(name, breaker_config)
                self.logger.info(f"Created circuit breaker: {name}")
            
            return self.circuit_breakers[name]
    
    def remove_circuit_breaker(self, name: str):
        """移除熔断器"""
        with self._lock:
            if name in self.circuit_breakers:
                del self.circuit_breakers[name]
                self.logger.info(f"Removed circuit breaker: {name}")
    
    def get_all_stats(self) -> Dict[str, Dict[str, Any]]:
        """获取所有熔断器统计信息"""
        with self._lock:
            return {name: breaker.get_stats() for name, breaker in self.circuit_breakers.items()}
    
    def reset_all(self):
        """重置所有熔断器"""
        with self._lock:
            for breaker in self.circuit_breakers.values():
                breaker.reset()
            self.logger.info("Reset all circuit breakers")

# 全局熔断器管理器
_circuit_breaker_manager: Optional[CircuitBreakerManager] = None

def get_circuit_breaker_manager() -> CircuitBreakerManager:
    """获取全局熔断器管理器"""
    global _circuit_breaker_manager
    if _circuit_breaker_manager is None:
        _circuit_breaker_manager = CircuitBreakerManager()
    return _circuit_breaker_manager

def set_circuit_breaker_manager(manager: CircuitBreakerManager):
    """设置全局熔断器管理器"""
    global _circuit_breaker_manager
    _circuit_breaker_manager = manager

# 装饰器
def circuit_breaker(
    name: str = None,
    failure_threshold: int = 5,
    success_threshold: int = 3,
    timeout: float = 60.0,
    expected_exception: type = Exception,
    monitor_window: float = 60.0,
    min_requests: int = 5,
    failure_rate_threshold: float = 0.5,
    config_name: str = None
):
    """熔断器装饰器"""
    def decorator(func: Union[F, AsyncF]) -> Union[F, AsyncF]:
        breaker_name = name or f"{func.__module__}.{func.__name__}"
        
        # 创建配置
        config = None
        if not config_name:
            config = CircuitBreakerConfig(
                failure_threshold=failure_threshold,
                success_threshold=success_threshold,
                timeout=timeout,
                expected_exception=expected_exception,
                monitor_window=monitor_window,
                min_requests=min_requests,
                failure_rate_threshold=failure_rate_threshold
            )
        
        # 获取熔断器
        manager = get_circuit_breaker_manager()
        breaker = manager.get_circuit_breaker(breaker_name, config)
        
        if asyncio.iscoroutinefunction(func):
            @functools.wraps(func)
            async def async_wrapper(*args, **kwargs):
                return await breaker.call_async(func, *args, **kwargs)
            return async_wrapper
        else:
            @functools.wraps(func)
            def sync_wrapper(*args, **kwargs):
                return breaker.call(func, *args, **kwargs)
            return sync_wrapper
    
    return decorator

# 预定义配置
QUICK_CIRCUIT_BREAKER = CircuitBreakerConfig(
    failure_threshold=3,
    success_threshold=2,
    timeout=30.0,
    monitor_window=30.0,
    min_requests=3,
    failure_rate_threshold=0.6
)

STANDARD_CIRCUIT_BREAKER = CircuitBreakerConfig(
    failure_threshold=5,
    success_threshold=3,
    timeout=60.0,
    monitor_window=60.0,
    min_requests=5,
    failure_rate_threshold=0.5
)

TOLERANT_CIRCUIT_BREAKER = CircuitBreakerConfig(
    failure_threshold=10,
    success_threshold=5,
    timeout=120.0,
    monitor_window=120.0,
    min_requests=10,
    failure_rate_threshold=0.7
)

# 示例使用
if __name__ == "__main__":
    import asyncio
    import random
    
    # 注册配置
    manager = get_circuit_breaker_manager()
    manager.register_config("quick", QUICK_CIRCUIT_BREAKER)
    manager.register_config("standard", STANDARD_CIRCUIT_BREAKER)
    manager.register_config("tolerant", TOLERANT_CIRCUIT_BREAKER)
    
    # 使用装饰器
    @circuit_breaker(name="external_api", config_name="quick")
    def call_external_api():
        if random.random() < 0.7:  # 70%概率失败
            raise ConnectionError("API call failed")
        return "API response"
    
    @circuit_breaker(name="database_query", failure_threshold=3, timeout=30.0)
    async def query_database():
        await asyncio.sleep(0.1)
        if random.random() < 0.6:  # 60%概率失败
            raise TimeoutError("Database timeout")
        return "Query result"
    
    # 手动使用熔断器
    async def manual_circuit_breaker_example():
        breaker = manager.get_circuit_breaker("manual_service", STANDARD_CIRCUIT_BREAKER)
        
        for i in range(10):
            try:
                if random.random() < 0.5:
                    raise Exception("Service failure")
                
                result = breaker.call(lambda: f"Service call {i}")
                print(f"Success: {result}")
                
            except CircuitBreakerOpenError as e:
                print(f"Circuit breaker open: {e}")
                await asyncio.sleep(1)
            except Exception as e:
                print(f"Service error: {e}")
    
    # 运行示例
    async def main():
        # 测试API调用
        for i in range(10):
            try:
                result = call_external_api()
                print(f"API call {i}: {result}")
            except CircuitBreakerOpenError as e:
                print(f"API circuit breaker open: {e}")
            except Exception as e:
                print(f"API error: {e}")
            
            await asyncio.sleep(0.5)
        
        # 测试数据库查询
        for i in range(5):
            try:
                result = await query_database()
                print(f"DB query {i}: {result}")
            except CircuitBreakerOpenError as e:
                print(f"DB circuit breaker open: {e}")
            except Exception as e:
                print(f"DB error: {e}")
            
            await asyncio.sleep(0.5)
        
        # 打印统计信息
        stats = manager.get_all_stats()
        for name, stat in stats.items():
            print(f"\nCircuit Breaker '{name}' Stats:")
            print(f"  State: {stat['state']}")
            print(f"  Total requests: {stat['total_requests']}")
            print(f"  Failure rate: {stat['failure_rate']:.2%}")
            print(f"  Consecutive failures: {stat['consecutive_failures']}")
    
    asyncio.run(main())
